package svc.elib.analysis;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;

import svc.elib.db.Author;
import svc.elib.db.Database;
import svc.elib.db.Paper;
import svc.elib.socnet.ConnectedComponents;
import svc.elib.socnet.EvolutionarySnapshots;
import svc.elib.socnet.Link;
import svc.elib.socnet.Net;
import svc.elib.socnet.SocConstructor;

public class ReturningVisitors {

	private Net[] snaps;
	private int startYear;
	private Database db;
	
	// link counters
	private int oldOldLinks = 0;
	private int oldNewLinks = 0;
	private int newNewLinks = 0;
    private int previousLinks = 0;
	
	public ReturningVisitors(Net net, Database db) {
		this.db = db;
		EvolutionarySnapshots es = new EvolutionarySnapshots(net);
		snaps = es.getSnapshots();
		startYear = es.getStartYear();
	}
	
	public void determineWithTimeFrame(String out, int startIndex, int timeFrame) 
		throws IOException
	{
		PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(out)));
		pw.println("year,newAuthors,returningAuthors,newLinks,OLD_OLD_links,OLD_NEW_links,NEW_NEW_links");
			
		for (int i = startIndex; i < snaps.length - timeFrame; i += timeFrame) {
			int currentYear = startYear + i;
			int nextYear = startYear + i + timeFrame;
				
			System.out.println("CurrentYear: " + currentYear + ", nextYear: " + nextYear);
				
			Net current = snaps[i];
			Net next = snaps[i + timeFrame];
				
			int numAuthors = determineAuthors(currentYear, nextYear);
			int numNewAuthors = determineNewAuthors(current, next);
			double returningAuthors = 0.0;
			if (numAuthors != 0)
				returningAuthors = (double) (numAuthors - numNewAuthors) / (double) numAuthors;
				
			determineLinks(current, next);
			double oldOldF = 0, oldNewF = 0, newNewF = 0;
			double linkSum = oldOldLinks + oldNewLinks + newNewLinks;
			if (linkSum > 0) {
				oldOldF = oldOldLinks / linkSum;
			    oldNewF = oldNewLinks / linkSum;
			    newNewF = newNewLinks / linkSum;
			}
				
			pw.println(nextYear + ", " + numAuthors + ", " + returningAuthors + ", " + linkSum + ", " +
				           oldOldF + ", " + oldNewF + ", " + newNewF);
				
			determineLinks(current, next);
		}
		
		
		pw.close();
	}
	
	public void determine(String out, String out2) 
		throws IOException
	{
		PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(out)));
		pw.println("year,newAuthors,returningAuthors,newLinks,OLD_OLD_links,OLD_NEW_links,NEW_NEW_links");
		
		PrintWriter pw2 = new PrintWriter(new BufferedWriter(new FileWriter(out2)));
		pw2.println("year,authorsNonIsolated,oldAuthors,newAuthors,links,previousLinks,OldOldLinks,OldNewLinks,NewNewLinks");
		
		for (int i = 0; i < snaps.length - 1; i++) {
			int nextYear = startYear + i + 1;
						
			Net current = snaps[i];
			Net next = snaps[i + 1];
			
			int numNodes = next.getNumNodes();
			numNodes -= next.getNumIsolated();
			int numLinks = next.getNumLinks();
			
			int numAuthors = determineAuthors(nextYear);
			int numNewAuthors = determineNewAuthors(current, next);
			int numNewNonIsolatedAuthors = determineNewAuthorsExcludeIsolated(current, next);
			double returningAuthors = 0.0;
			if (numAuthors != 0)
				returningAuthors = (double) (numAuthors - numNewAuthors) / (double) numAuthors;
			
			determineLinks(current, next);
			double oldOldF = 0, oldNewF = 0, newNewF = 0;
			double linkSum = oldOldLinks + oldNewLinks + newNewLinks;
			if (linkSum > 0) {
				oldOldF = oldOldLinks / linkSum;
			    oldNewF = oldNewLinks / linkSum;
			    newNewF = newNewLinks / linkSum;
			}
			
			pw.println(nextYear + ", " + numAuthors + ", " + returningAuthors + ", " + linkSum + ", " +
			           oldOldF + ", " + oldNewF + ", " + newNewF);
			
			pw2.println(nextYear + ", " + numNodes + "," + (numNodes - numNewNonIsolatedAuthors) + ", " + numNewAuthors + ", " + numLinks + ", " + previousLinks + ", " + oldOldLinks + ", " + oldNewLinks + ", " + newNewLinks);
			
			determineLinks(current, next);
		}
		
		pw.close();
		pw2.close();
	}
	
	
	public void determine3(String out) 
		throws IOException
	{
		PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(out)));
		
		for (int i = 0; i < snaps.length - 1; i++) {
			int nextYear = startYear + i + 1;
						
			Net current = snaps[i];
			Net next = snaps[i + 1];
			
			
			determineLinks2(current, next);
			double oldOldF = 0, oldNewF = 0, newNewF = 0;
			double linkSum = oldOldLinks + oldNewLinks + newNewLinks;
			if (linkSum > 0) {
				oldOldF = oldOldLinks / linkSum;
			    oldNewF = oldNewLinks / linkSum;
			    newNewF = newNewLinks / linkSum;
			}
			
			System.out.println(nextYear + ", " + oldOldLinks + ", " + oldNewLinks + ", " + newNewLinks);
		}
		
		pw.close();
	}
	
	
	private int determineNewAuthors(Net current, Net next) {
		Iterator<Author> ait = next.getGraph().getVertices().iterator();
	
		int numNewAuthors = 0;
		while (ait.hasNext()) {
			Author a = ait.next();
			if (!current.getGraph().containsVertex(a))
				++numNewAuthors;
		}
		return numNewAuthors;
	}
	
	private int determineNewAuthorsExcludeIsolated(Net current, Net next) {
		Iterator<Author> ait = next.getGraph().getVertices().iterator();
	
		int numNewAuthors = 0;
		while (ait.hasNext()) {
			Author a = ait.next();
			if (next.getGraph().degree(a) == 0)
				continue;
			
			if (!current.getGraph().containsVertex(a))
				++numNewAuthors;
		}
		return numNewAuthors;
	}
	
	/**
	 * How many authors published papers in a given year?
	 * 
	 * @param year
	 * @return
	 */
	private int determineAuthors(int year) {
		HashMap<Integer, LinkedList<Paper>> yearIndex = db.getYearIndex();
		LinkedList<Paper> pl = yearIndex.get(year);
		
		if (pl == null)
			return 0;
		
		HashSet<Author> hs = new HashSet<Author>();
		
		for (int i = 0; i < pl.size(); i++) {
			Paper p = pl.get(i);
			LinkedList<Author> al = p.getAuthors();
			for (int j = 0; j < al.size(); j++) {
				hs.add(al.get(j));
			}
		}
		
		return hs.size();
	}
	
	private int determineAuthors(int start, int end) {
		HashMap<Integer, LinkedList<Paper>> yearIndex = db.getYearIndex();
		HashSet<Author> hs = new HashSet<Author>();
		
		for (int y = start + 1; y <= end; y++) {
			LinkedList<Paper> pl = yearIndex.get(y);
		
			if (pl == null)
				continue;
		
			for (int i = 0; i < pl.size(); i++) {
				Paper p = pl.get(i);
				LinkedList<Author> al = p.getAuthors();
				for (int j = 0; j < al.size(); j++) {
					hs.add(al.get(j));
				}
			}
		}
		
		return hs.size();
	}
	
	private void determineLinks(Net current, Net next) {
		Collection<Link> nextLinks = next.getGraph().getEdges();		
		oldOldLinks = oldNewLinks = newNewLinks = previousLinks = 0;
		
		Iterator<Link> it = nextLinks.iterator();
		while (it.hasNext()) {
			Link l = it.next();
			Author a1 = l.getSrc();
			Author a2 = l.getDst();
			//int w = l.getWeight();
			
			if (current.connected(a1, a2)) {
				previousLinks++;
			}
			else
			if (current.getGraph().containsVertex(a1) && current.getGraph().containsVertex(a2)) 
				oldOldLinks++;
			else
			if (current.getGraph().containsVertex(a1) || current.getGraph().containsVertex(a2))
				oldNewLinks++;
			else {
				newNewLinks++;
			}
		}
	}
	
	private void determineLinks2(Net current, Net next) {
		Collection<Link> nextLinks = next.getGraph().getEdges();		
		oldOldLinks = oldNewLinks = newNewLinks = previousLinks = 0;
		
		Iterator<Link> it = nextLinks.iterator();
		while (it.hasNext()) {
			Link l = it.next();
			Author a1 = l.getSrc();
			Author a2 = l.getDst();
			int w = l.getWeight();
			
			if (current.connected(a1, a2)) {
				Link lCurrent = current.getLink(a1, a2);
				if (lCurrent.getWeight() == w)
					continue; // old link
				else
					oldOldLinks += w - lCurrent.getWeight();
			}
			else
			if (current.getGraph().containsVertex(a1) && current.getGraph().containsVertex(a2)) {
				oldOldLinks += w;
			}
			else
			if (current.getGraph().containsVertex(a1) || current.getGraph().containsVertex(a2)) {
				oldNewLinks++;
				oldOldLinks += w - 1;
			}
			else {
				newNewLinks++;
				oldOldLinks += w - 1;
			}
		}
	}
	
	public static void main(String[] args) 
		throws IOException
	{
		Database db = new Database("eLibData.csv", 1932, 2011);
		SocConstructor soc = new SocConstructor(db);
		Net net = soc.getNet();
	
		ReturningVisitors rt = new ReturningVisitors(net, db);
		rt.determine3("results/collaborationType.csv");
		
		/*
		rt.determine("results/ReturningVisitors.csv", "results/Collaboration.csv");
		rt.determineWithTimeFrame("results/ReturningVisitors_5.csv", 4, 5);
		
		ConnectedComponents cs = new ConnectedComponents(net);
		cs.resolveComponents();
		Net largestComp = cs.getComponents().get(0);
		LinkedList<Paper> papers = new LinkedList<Paper>();
		
		Iterator<Author> ait = largestComp.getGraph().getVertices().iterator();
		while (ait.hasNext()) {
			Author a = ait.next();
			LinkedList<Paper> pla = a.getPapers();
			papers.addAll(pla);
		}
		
		Database dbLargest = new Database("dbLargest", papers);
		rt = new ReturningVisitors(largestComp, dbLargest);
		rt.determine("results/ReturingVisitorsLargestComp.csv", "results/CollaborationLargest.csv");
		rt.determineWithTimeFrame("results/ReturningVisitorsLargestComp_5.csv", 4, 5);
		*/
	}
}
